package com.toy.core.security.login.web;

import static org.apache.commons.lang.StringUtils.isNotBlank;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import jcifs.Config;
import jcifs.UniAddress;
import jcifs.http.NtlmSsp;
import jcifs.smb.NtlmPasswordAuthentication;
import jcifs.smb.SmbSession;
import jcifs.util.Base64;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestMapping;

import com.google.common.collect.Lists;
import com.toy.core.client.SystemFacade;
import com.toy.core.config.SysConfig;
import com.toy.core.security.AuthenticatedType;
import com.toy.core.security.Authentication;
import com.toy.core.security.UsernamePasswordAuthentication;
import com.toy.core.security.authenticate.AuthenticationProvider;
import com.toy.core.security.exception.AuthenticationException;
import com.toy.core.util.CookieUtils;
import com.toy.core.web.BaseController;

@Controller
@RequestMapping("/client/ntlm")
public class NtlmController extends BaseController implements InitializingBean{
	/**
	 * Logger for this class
	 */
	private static final Logger logger = Logger.getLogger(NtlmController.class);
	
	@Autowired
	private AuthenticationProvider authenticationProvider;
	
	@Autowired
	private SysConfig sysConfig;
	
	private boolean loadBalance = false;
    private boolean enableBasic;
    private boolean insecureBasic;
	
    private static final String SESSION_DOMAINCONTROLLER = "_domainController_";

	public void afterPropertiesSet() throws Exception {
		Config.setProperty( "jcifs.smb.client.soTimeout", "100" );//100
		Config.setProperty( "jcifs.netbios.cachePolicy", "1200" );
		Config.setProperty( "jcifs.smb.lmCompatibility", "0" );
        Config.setProperty( "jcifs.smb.client.useExtendedSecurity", "false" );
        Config.setProperty("jcifs.util.loglevel", "6");
        Config.setProperty("jcifs.http.loadBalance", Boolean.toString(loadBalance));
        
        enableBasic = Boolean.valueOf(Config.getProperty("jcifs.http.enableBasic")).booleanValue();
        insecureBasic = Boolean.valueOf(Config.getProperty("jcifs.http.insecureBasic")).booleanValue();
	}
	
	@RequestMapping("/autologin")
	public String autoLogin(HttpServletRequest request,
			HttpServletResponse response){
		String target = request.getParameter("target");
		StringBuffer scripts = new StringBuffer();
		String ctx = request.getContextPath();
		DomainConfig domainConfig = DomainConfig.getInstance();
		try {
			String randomDC = domainConfig.getRandomDomainController();
			NtlmPasswordAuthentication ntlm = negotiateJCIFS(request, response,randomDC,true);
			if ( ntlm == null )return null;
			
			//以下为验证部分
			String domainController = domainConfig.getDomainController(ntlm.getDomain());
			if (logger.isDebugEnabled()) {
				logger.debug("客户端信息="+ntlm.getDomain() + ":"+ntlm.getUsername()+"<"+domainController+">"); 
			}
			if (StringUtils.isBlank(domainController)){
				scripts.append("alert(\"当前用户不在"+ntlm.getDomain()+"域中，请输入正确的域账号和密码登陆系统\");");
				scripts.append("document.execCommand('ClearAuthenticationCache');");
				scripts.append("location.href='"+ctx + "/client/ntlm/switchLogin';");
				super.writeScripts(response, scripts.toString());
				return null;
			}
			//验证部分结束
			request.getSession().setAttribute(SESSION_DOMAINCONTROLLER, domainController);
			scripts.append("location.href='"+request.getContextPath()+"/client/ntlm/ntlmLogin");
			if (isNotBlank(target)){scripts.append("?target="+target);};
			scripts.append("';");
			super.writeScripts(response, scripts.toString());
			return null;
		} catch (Exception e) {
			e.printStackTrace();
			scripts.append("alert(\"系统错误,请通知管理员\");");
			scripts.append("document.execCommand('ClearAuthenticationCache');");
			scripts.append("location.href='"+ctx + "/client/ntlm/switchLogin';");
			super.writeScripts(response, scripts.toString());
			return null;
		}
	}
	
	/**
	 * 自动登陆验证
	 */
	@RequestMapping("/ntlmLogin")
	public String ntlmLogin(HttpServletRequest request,
			HttpServletResponse response){
		String target = request.getParameter("target");
		String ctx = request.getContextPath();
		StringBuffer scripts = new StringBuffer();
		try {
			String domainController =(String)request.getSession().getAttribute(SESSION_DOMAINCONTROLLER);
			if (logger.isDebugEnabled()) {logger.debug("domainController="+domainController);}
			if (StringUtils.isBlank(domainController)){
				scripts.append("alert(\"系统错误,请通知管理员\");");
				scripts.append("document.execCommand('ClearAuthenticationCache');");
				scripts.append("location.href='"+ctx + "/client/ntlm/switchLogin';");
				super.writeScripts(response, scripts.toString());
				return null;
			}
			
			//用户登陆，开始验证密码
			NtlmPasswordAuthentication ntlm = negotiateJCIFS(request, response,domainController,false);
			if ( ntlm == null )return null;

			//登陆验证
			UsernamePasswordAuthentication authentication = new UsernamePasswordAuthentication(ntlm.getUsername(),AuthenticatedType.NTLM);
			authentication.setAttribute(Authentication.ATTR_REMOTEADDR, request.getRemoteAddr());
			authentication.setAttribute(Authentication.ATTR_SESSIONID, request.getSession().getId());
			try {
				Authentication newAuthentication = authenticationProvider.authenticate(authentication);
				CookieUtils.saveAuthentication(newAuthentication, request, response);
			} catch (AuthenticationException e) {
				if (logger.isDebugEnabled()) {logger.debug("AuthenticationException=" + e);}
				scripts.append("alert(\""+ntlm.getDomain()+"\\\\"+ntlm.getUsername()+"用户登陆系统错误:"+e.getMessage()+"\");");
				scripts.append("document.execCommand('ClearAuthenticationCache');");
				scripts.append("location.href='"+ctx + "/client/ntlm/switchLogin';");
				super.writeScripts(response, scripts.toString());
				return null;
			}
			
			// 登录成功跳转到子系统首页
			if (StringUtils.isNotBlank(target)) {
				// 登录成功跳转到指定的目标页面
				return "redirect:"+target;
			} else {
				// 登录成功跳转到子系统首页
				return "redirect:"+sysConfig.getHomePageUrl();
			}
		} catch (Exception e) {
			if (logger.isDebugEnabled()) {logger.debug("e=" + e);}
			e.printStackTrace();
			scripts.append("alert(\"系统错误,请通知管理员\");");
			scripts.append("document.execCommand('ClearAuthenticationCache');");
			scripts.append("location.href='"+ctx + "/client/ntlm/switchLogin';");
			super.writeScripts(response, scripts.toString());
			return null;
		}
	}
	
	/**
	 * 切换用户
	 */
	@RequestMapping("/switchLogin")
	public String switchLogin(HttpServletRequest request,
			HttpServletResponse response){
		String target = request.getParameter("target");
		String ctx = request.getContextPath();
		StringBuffer scripts = new StringBuffer();
		DomainConfig domainConfig = DomainConfig.getInstance();
		try {
			String auth = request.getHeader("Authorization");
			if ( StringUtils.isBlank(auth)){
				//弹出验证框
				response.setStatus(HttpServletResponse .SC_UNAUTHORIZED);   
				response.setHeader("WWW-Authenticate", "Basic realm=\""+domainConfig.getRealm()+"\"");   
				response.flushBuffer();   
				return null;   
			}
			
			if(auth.startsWith("Basic ")){
				//用户名和密码解密
				String username_pw = new String(Base64.decode(auth.substring(6)));
				StringBuffer error = new StringBuffer();
				
				String[] array = username_pw.split(":");
				if ( array != null && array.length == 2){
					String userDomain = array[0];
					String password = array[1];
					//以下为验证部分
					String princal = null,domainController = null,domain = null;
					int domainSize = domainConfig.getDomainSize();
					if ( domainSize == 1){
						princal = userDomain;
						domainController = domainConfig.getRandomDomainController();
						domain = domainConfig.getOnlyDomain();
					}else{
						if( userDomain.indexOf("\\") == -1){
							error.append("alert(\"用户格式错误,正确的格式是:域\\\\用户名\");");
						}else{
							String[] user_domain = userDomain.split("\\\\");
							if (user_domain == null || user_domain.length != 2){
								error.append("alert(\"用户格式错误,正确的格式是:域\\\\用户名\");");
							}else{
								domain = user_domain[0];
								princal = user_domain[1];
								domainController = domainConfig.getDomainController(domain);
							}
						}
					}
					
					if ( error.length() == 0 ){
						if (isNotBlank(domainController)){
							//统一将域名小写处理
							boolean validate = validateDomainUser(domainController,princal, password);
							if (validate){
								//登陆验证
								UsernamePasswordAuthentication authentication = new UsernamePasswordAuthentication(princal,AuthenticatedType.NTLM);
								authentication.setAttribute(Authentication.ATTR_REMOTEADDR, request.getRemoteAddr());
								authentication.setAttribute(Authentication.ATTR_SESSIONID, request.getSession().getId());
								try {
									Authentication newAuthentication = authenticationProvider.authenticate(authentication);
									CookieUtils.saveAuthentication(newAuthentication, request, response);
								} catch (AuthenticationException e) {
									if (logger.isDebugEnabled()) {logger.debug("AuthenticationException=" + e);}
									scripts.append("alert(\""+domain+"\\\\"+princal+"用户登陆系统错误:"+e.getMessage()+"\");");
									scripts.append("document.execCommand('ClearAuthenticationCache');");
									scripts.append("location.href='"+ctx + "/client/ntlm/switchLogin';");
									super.writeScripts(response, scripts.toString());
									return null;
								}
								
								// 登录成功跳转到子系统首页
								if (StringUtils.isNotBlank(target)) {
									// 登录成功跳转到指定的目标页面
									return "redirect:"+target;
								} else {
									// 登录成功跳转到子系统首页
									return "redirect:"+sysConfig.getHomePageUrl();
								}
							}else{
								error.append("alert(\"您输入的用户名或密码错误\");");
							}
						}else{
							error.append("alert(\"当前用户不在"+domain+"域中，请输入正确的域账号和密码登陆系统\");");
						}
					}	
				}else{
					error.append("alert(\"登陆名和密码都不能为空\");");
				}
				scripts.append(error.toString());
				scripts.append("document.execCommand('ClearAuthenticationCache');");
				scripts.append("location.href='"+ctx + "/client/ntlm/switchLogin';");
				super.writeScripts(response, scripts.toString());
				return null;
			}else{
				response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);   
				response.setHeader("WWW-Authenticate", "Basic realm=\""+domainConfig.getRealm()+"\"");   
				response.flushBuffer();   
				return null;   
			}
		} catch (Exception e) {
			if (logger.isDebugEnabled()) {logger.debug("e=" + e);}
			scripts.append("alert(\"系统错误,请通知管理员\");");
			scripts.append("document.execCommand('ClearAuthenticationCache');");
			scripts.append("location.href='"+ctx + "/erm/ntlm/switchLogin';");
			super.writeScripts(response, scripts.toString());
			return null;
		}
	}
	
	 /**
     * 通过cifs获得登陆的用户名,用于自动登录,选择进行域验证
     * @param req
     * @param resp
     * @param domainController 域的域控制地址，通常为IP地址
     * @param skipDomainValidate 跳过域验证
     * @return
     * @throws Exception
     */
    protected NtlmPasswordAuthentication negotiateJCIFS( HttpServletRequest req,
                HttpServletResponse resp,String domainController,
                boolean skipDomainValidate) throws Exception {
		NtlmPasswordAuthentication ntlm = null;
		UniAddress dc = null;
        String msg = req.getHeader( "Authorization" );
        boolean offerBasic = enableBasic && (insecureBasic || req.isSecure());
        if( msg != null && (msg.startsWith( "NTLM " ) || (offerBasic && msg.startsWith("Basic ")))) {
			if (msg.startsWith("NTLM ")) {
				dc = UniAddress.getByName( domainController,true );
                byte[] challenge = SmbSession.getChallenge( dc );
                if(( ntlm = NtlmSsp.authenticate( req, resp, challenge )) == null ) {
                	return null;
                }
	        }
			if ( skipDomainValidate ){
				return ntlm;
			}
	        try {
                SmbSession.logon( dc, ntlm );
                return ntlm;
            } catch( Exception e ) {
            	System.out.println(e.getClass().getName()+":"+e.getMessage());
            	resp.setHeader( "WWW-Authenticate", "NTLM" );
                resp.setStatus( HttpServletResponse.SC_UNAUTHORIZED );
                resp.setContentLength(0); 
                resp.flushBuffer();
                return null;
            }
        }else{
        	resp.setHeader( "WWW-Authenticate", "NTLM" );
            resp.setStatus( HttpServletResponse.SC_UNAUTHORIZED );
            resp.setContentLength(0);
            resp.flushBuffer();
            return null;
        }
    }
    
    /**
	 * 验证域用户名和密码
	 */
	private boolean validateDomainUser(String domainController,String username,String password){
		try {
			UniAddress mydomaincontroller = UniAddress.getByName( domainController );
			NtlmPasswordAuthentication mycreds1 = new NtlmPasswordAuthentication( "", username, password );
			SmbSession.logon( mydomaincontroller, mycreds1 );
			return true;
		} catch (Exception e) {
			if (logger.isDebugEnabled()) {
				logger.debug("validateDomainUser(String, String, String) - Exception e=" + e); //$NON-NLS-1$
			}
			
		} 
		return false;
	}
	
}
class DomainConfig{
	
	private static DomainConfig instance = null;
	
	private Map<String,String> domains = new HashMap<String,String>();
	
	private DomainConfig() {
		String ntlmConfig = SystemFacade.getSysConfig().getNtlmConfig();
		String[] dsArray = ntlmConfig.split(";");
		for (int i = 0; i < dsArray.length; i++) {
			String[] domain = dsArray[i].split("\\\\");
			domains.put(domain[0].toLowerCase(), domain[1]);
		}
	}
	
	public static DomainConfig getInstance(){
		if ( instance == null ){
			instance = new DomainConfig();
		}
		return instance;
	}
	
	
	public String getDomainController(String domain){
		return domains.get(domain.toLowerCase());
	}
	
	/**
	 * 获得随机的域控制器地址，当单域环境，返回单域控制器
	 * @return
	 */
	public String getRandomDomainController(){
		ArrayList<String> controllers = Lists.newArrayList(domains.values());
		int size = controllers.size();
		if ( size == 1){
			return controllers.get(0);
		}else{
			long random = System.currentTimeMillis()%size;
			return controllers.get(Integer.parseInt(String.valueOf(random)));
		}
	}
	
	public String getRealm(){
		return ArrayUtils.toString(domains.keySet().toArray(new String[domains.keySet().size()]));
	}
	
	public int getDomainSize(){
		return domains.size();
	}
	
	/**
	 * 单域环境下，返回单域名
	 * @return
	 */
	public String getOnlyDomain(){
		Assert.state(domains.size()==1);
		return domains.keySet().iterator().next();
	}
	
}